/*
 * Copyright 2016-2019 yoara
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package yhao.infra.web.common.security.encrypt;


import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import yhao.infra.apilist.JsonCommonCodeEnum;
import yhao.infra.apilist.ValidationForm;
import yhao.infra.common.util.encrypt.RSAUtil;
import yhao.infra.web.common.security.encrypt.annotation.CheckRSAEncryptAnnotation;
import yhao.infra.web.common.security.encrypt.annotation.InitRSAEncryptAnnotation;
import yhao.infra.web.common.security.encrypt.exception.RSAEncryptException;
import yhao.infra.web.common.security.encrypt.helper.RSAEncryptHelper;
import yhao.infra.web.common.util.CommonWebUtil;

import javax.servlet.http.HttpServletRequest;
import java.security.KeyPair;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;

/**
 * 用于校验RSA 公钥
 *
 * <p>标记了注解{@link CheckRSAEncryptAnnotation}  的展示层方法，在执行方法前会判断加密信息
 * @author yoara
 */
@Aspect
@Component
public class RSAEncryptAop{
	/**token最大存活时间，单位为秒**/
	public final static int ENCRYPT_TOKEN_EXPIRE = 60*30;
	@Autowired
	protected RSAEncryptHelper rsaEncryptHelper;

	/** 检查是否需要初始化RSA 公钥 **/
	@Around(value = "@annotation(init)")
	public Object checkInitRSAEncrypt(ProceedingJoinPoint joinPoint, InitRSAEncryptAnnotation init) throws Throwable {
        KeyPair keyPair = RSAUtil.makeKeyPair();
        // 公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

        String key = rsaEncryptHelper.putRSAPriveteKey(keyPair,init.expire());
        HttpServletRequest request = CommonWebUtil.getRequest();
        request.setAttribute(RSAEncryptHelper.RSA_PARAM_KEY,key);
        request.setAttribute(RSAEncryptHelper.RSA_PUBLIC_MODULES,publicKey.getModulus().toString(16));
        request.setAttribute(RSAEncryptHelper.RSA_PUBLIC_EXPONENT,publicKey.getPublicExponent().toString(16));
		return joinPoint.proceed();
	}

	/** 检查是否需要校验RSA 公钥 **/
	@Around(value = "@annotation(ca)")
	public Object checkRSAEncrypt(ProceedingJoinPoint joinPoint, CheckRSAEncryptAnnotation ca) throws Throwable {
        String sign = CommonWebUtil.getString(RSAEncryptHelper.RSA_PARAM_ENCRYPTSTR,null);
        boolean onlyMD5 = CommonWebUtil.getBoolean(RSAEncryptHelper.RSA_PARAM_ONLYMD5,false);
        String key = CommonWebUtil.getString(RSAEncryptHelper.RSA_PARAM_KEY,null);
        HttpServletRequest request = CommonWebUtil.getRequest();
        ValidationForm form = null;
        for(Object arg:joinPoint.getArgs()){
            if(arg instanceof ValidationForm){
                form = (ValidationForm)arg;
                break;
            }
        }
        if(!EncryptCheckResult.MATCHED.equals(
                rsaEncryptHelper.rsaCheck(sign,rsaEncryptHelper.makeCheckMap(request,form),key,onlyMD5))){
            return JSON.toJSONString(refused(ca));
        }

		return joinPoint.proceed();
	}

	/**校验不通过行为**/
	private Map<String, Object> refused(CheckRSAEncryptAnnotation annotation) throws Exception {
		switch (annotation.dealType()){
			case JSON:
                Map<String, Object> jsonResult = new HashMap<>();
                jsonResult.put("status", JsonCommonCodeEnum.E0010.getStatus());
                jsonResult.put("message", JsonCommonCodeEnum.E0010.getMessage());
                jsonResult.put("result", null);
				return jsonResult;
			default:
				throw new RSAEncryptException("RSA encrypt invalid");
		}
	}
}